package com.dly.hope.interceptor;

import com.dly.hope.annotation.LoginRole;
import com.dly.hope.constant.ExceptionConstant;
import com.dly.hope.constant.RedisConstant;
import com.dly.hope.dao.PermissionsDao;
import com.dly.hope.dao.RoleDao;
import com.dly.hope.modle.Permissions;
import com.dly.hope.modle.Role;
import com.dly.hope.servce.JwtService;
import com.dly.hope.type.PermissionsType;
import com.dly.hope.type.RoleType;
import com.dly.hope.utils.CommonUtil;
import com.dly.hope.utils.ExceptionPropertiesUtil;
import com.dly.hope.utils.RedisUtil;
import com.dly.hope.vo.UserDTO;
import com.dly.hope.webSocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author linyu.dai
 * @project myserve
 * @description 登入角色拦截器
 * @creat 2022/8/29 20:18:54
 */
@Component
@Slf4j
public class JwtCheckInterceptor implements AsyncHandlerInterceptor {

    @Value("${jwt.header}")
    private String jwtHeader;

    @Resource
    private JwtService jwtService;

    @Resource
    private RoleDao roleDao;

    @Resource
    private PermissionsDao permissionsDao;

    @Resource
    private RedisUtil redisUtil;

    @Value("${jwt.redisExpireTime}")
    private long redisExpireTime;

    @Value("${jwt.expireTime}")
    private long tokenExpireTime;

    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
        //过滤预检请求options 对象
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            //获取方法上的注解
            LoginRole annotation = getAnnotation(handlerMethod);
            if (Objects.nonNull(annotation)) {

                //存在注解需要验证登入
                String token = CommonUtil.getToken(request, jwtHeader);
                UserDTO userDTO = jwtService.unSign(token);
                log.info(userDTO.toString());

                //判断是否登入过 废弃数据库的方法
                Optional.ofNullable(redisUtil.getStr(userDTO.getId()))
                        .map(lastLogin -> {
                            if (lastLogin.equals(userDTO.getLastLogin())) {
                                //表示是一次登入

                                //再去通过redis 判断是否还在 更换token
                                if (Boolean.FALSE.equals(redisUtil.exists(RedisConstant.TOKEN + token))) {
                                    //去掉之前的登入记录
                                    String nowLogin = UUID.randomUUID().toString();
                                    //换掉登入记录
                                    userDTO.setLastLogin(nowLogin);
                                    //换成新的token
                                    String now = jwtService.sign(userDTO);
                                    redisUtil.setStr(RedisConstant.TOKEN + now, userDTO.getId(), redisExpireTime, TimeUnit.SECONDS);
                                    response.setHeader("Access-Control-Expose-Headers", jwtHeader);
                                    response.addHeader(jwtHeader, now);

                                }
                                return true;
                            }
                            return null;
                        }).orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.ANOTHER_PLACE_LOGIN));

                //验证角色 如果有的话
                if (!annotation.role()[0].equals(RoleType.NONE)) {
                    Set<RoleType> role = Stream.of(annotation.role()).collect(Collectors.toSet());
                    Set<RoleType> roles = roleDao.getRole(userDTO.getId()).stream().map(Role::getRoleType).collect(Collectors.toSet());
                    if (!roles.containsAll(role)) {
                        throw ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION);
                    }
                }

                //验证权限
                if (!annotation.permissions()[0].equals(PermissionsType.NONE)) {
                    Set<PermissionsType> permission = Stream.of(annotation.permissions()).collect(Collectors.toSet());
                    Set<PermissionsType> permissions = permissionsDao.getPermissions(userDTO.getId()).stream().map(Permissions::getPermissionsType).collect(Collectors.toSet());
                    if (!permissions.containsAll(permission)) {
                        throw ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION);
                    }
                }

                //刷新用户登入
                redisUtil.setStr(userDTO.getId(), userDTO.getLastLogin(), tokenExpireTime, TimeUnit.SECONDS);
            }
        }
        return true;
    }

    //查找对应类上的注解
    private LoginRole getAnnotation(HandlerMethod handlerMethod){
        return CommonUtil.getAnnotation(handlerMethod, LoginRole.class);
    }
}
